#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <filesystem>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <stdio.h>
#include "Mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;

    //日志的构成：1.构建日志信息 2.刷新落盘，即把日志信息保存显示到那里（保存到磁盘的文件or显示在显示器文件）

    //1.如果要把日志信息保存到文件中，需要默认的路径和文件名
    const std::string default_file_path="./log/";
    const std::string default_file_name="log.txt";

    //2.日志等级
    //枚举类的成员名字在枚举类的外部不可见，必须通过枚举类名访问，避免了名字冲突；不能直接将枚举类的值隐式转换为整数，需要显式转换
    enum class LogLevel{
        INFO=1, //正常信息
        DEBUG, //调试信息
        WARNING, //警告信息，不影响程序的运行
        ERROR, //代码出错，程序无法往后执行
        FATAL //致命错误，程序直接崩溃退出
    };

    std::string LevelToStr(LogLevel level){
        switch(level){
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::DEBUG:
                return "DEBUG";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";
            default:
                return "Unkown";
        }
    }

    //根据时间戳获取当前系统的时间
    std::string CurrentTime(){
        time_t timestamp = time(nullptr);
        struct tm curr;
        localtime_r(&timestamp, &curr);

        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d", 
            curr.tm_year+1900, //从1900开始算起的年份
            curr.tm_mon+1, //月份0-11
            curr.tm_mday,
            curr.tm_hour,
            curr.tm_min,
            curr.tm_sec
        );

        return buffer;
    }

    //3.刷新策略
    class LogStrategy{
    public:
        virtual ~LogStrategy() = default; //default表示显式要求编译器生成该函数的默认版本
        virtual void SyncLog(const std::string& message) = 0; //0用于声明一个纯虚函数，即该函数没有默认实现，包含纯虚函数的类叫做抽象类，不能实例化，派生类必须重写纯虚函数
    };
    //3.1刷新到显示器文件
    class ConsoleLogStrategy:public LogStrategy{
    public:
        ConsoleLogStrategy(){}
        ~ConsoleLogStrategy(){}
        void SyncLog(const std::string& message){
            //显示器也是共享资源，需要加锁保护
            MutexGuard mutexguard(_mutex);
            std::cout << message << std::endl;
        }
    private:
        Mutex _mutex;
    };
    //3.2保存到磁盘文件里
    class FileLogStrategy:public LogStrategy{
    public:
        FileLogStrategy(std::string filepath = default_file_path, std::string filename = default_file_name)
            :_filename(filename),
             _filepath(filepath)
        {
            MutexGuard mutexguard(_mutex);
            //确保文件是存在的
            if(std::filesystem::exists(_filepath)){
                //文件存在就直接返回
                return;
            }
            try{
                //不存在就创建
                std::filesystem::create_directories(_filepath);
            }
            catch(std::filesystem::filesystem_error &e){
                std::cerr << e.what() << std::endl;
            }
        }
        ~FileLogStrategy(){
        }
        void SyncLog(const std::string& message){
            MutexGuard mutexguard(_mutex);
            //输出文件流：把数据写入到文件中
            std::string log = _filepath+_filename;
            std::ofstream out(log, std::ios::app); //以追加的方式写入文件
            if(!out.is_open()){
                return;
            }
            out << message << std::endl;
            out.close();
        }
        
    private:
        std::string _filepath;
        std::string _filename;
        Mutex _mutex;
    };

    //日志类: 构建日志信息，根据刷新策略刷新
    class Logger{
    public:
        Logger(){
            //默认使用使用刷新到显示器的策略
            _logstrategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableConsoleLog(){
            _logstrategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableFileLog(){
            _logstrategy=std::make_shared<FileLogStrategy>();
        }
        ~Logger(){}

        //内部类:一个LogMessage对象对应一条完整的日志记录：固定部分+可变部分，即[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
        class LogMessage{
        public:
            LogMessage(LogLevel level, const std::string &filename, int line, Logger& logger)
                :_currtime(CurrentTime()),
                 _level(level),
                 _pid(getpid()),
                 _filename(filename),
                 _line(line),
                 _logger(logger)
            {
                std::stringstream ssbuffer;
                ssbuffer << "[" << _currtime << "] "
                         << "[" << LevelToStr(_level) << "] "
                         << "[" << _pid << "] "
                         << "[" << _filename << "] "
                         << "[" << _line << "] ";
                _loginfo = ssbuffer.str();
            }

            template<typename T>
            LogMessage& operator<<(const T& info){
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }

            //在临时对象释放时就自动刷新到显示器or文件
            ~LogMessage(){
                if(_logger._logstrategy){
                    _logger._logstrategy->SyncLog(_loginfo);
                }
            }
        private:
            std::string _currtime;          //当前的时间
            LogLevel _level;                //日志等级
            pid_t _pid;                     //进程pid
            std::string _filename;          //文件名
            int _line;                      //行号
            Logger &_logger;                //当前对象
            std::string _loginfo;           //一条完整的日志记录信息
        };

        //创建临时对象
        LogMessage operator()(LogLevel level, const std::string &filename, int line){
            return LogMessage(level, filename, line, *this);
        }

    private:
        std::shared_ptr<LogStrategy> _logstrategy;
    };

    Logger logger;
    #define LOG(Level) logger(Level, __FILE__, __LINE__)
    #define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()
    #define ENABLE_FILE_LOG() logger.EnableFileLog()

}